include("shared.lua")

-- Client Variables
local HighMenu = "High Scores Menu Place Holder Variable"
local joinMsg = "Place Holder For Join Message"
local joinMsgOpen = false
local showTabMenu = false
local closingScores = false
local openingScores = false
local cvInShip = false
local viewAng = Angle(0,0,0)
local angGoal = Angle(0,0,0)
local camPos = Vector(0,0,0)
local curCamPos = Vector(0,0,0)
local shipVel = Vector(0,0,0)
local lastViewCalc = RealTime()
local inThird = true
local deathCam = false
local scoresOpen = false
local mapAngle = Angle(0,-90,-135)
local scrAvg = (ScrW() + ScrH()) / 2
local mapWidth = scrAvg / 5
local mapHeight = scrAvg / 5
local mapPosX = (ScrW() - (mapWidth / 2)) - 10 
local mapPosY = (mapHeight / 2) + 10
local mapBorder = 1
mapScale = scrAvg / ((mapWidth * mapHeight) * 4)
local dotBase = 2 

local score = 0

local HighScores = {}
local asteroids = {}
local shots = {}
local mapData = {}

local lifeShips = lifeShips or {}
local lives = 3

--hook.Add( "CalcView", "InShipView", function( ply, origin, angles, fov )
function GM:CalcView(ply, origin, angles, fov)
	local view = {}
	
	if not cvInShip then return  end
	
	if deathCam then
		view.origin = camPos
		origin = camPos
		curCamPos = camPos
		view.angles = angGoal
		angles = angGoal
		viewAng = angGoal
		view.fov = fov
		return view
	end
	
	if inThird then
		
		local calcDelta = RealTime() - lastViewCalc
		lastViewCalc = RealTime()
		
		viewAng = LerpAngle(calcDelta * 3, viewAng, angGoal)
		view.angles = viewAng
		angles = viewAng

		curCamPos = LerpVector(calcDelta * 1.5, curCamPos, camPos)
		view.origin = curCamPos
		origin = curCamPos
	else
		view.origin = origin
		curCamPos = origin
		view.angles = angles
		viewAng = angles
	end
	
	view.fov = fov
  
	return view
	
end --)
  
usermessage.Hook("inShip", function(msgData) cvInShip = msgData:ReadBool() end)
usermessage.Hook("camPos", function(msgData) camPos = msgData:ReadVector() end) 
usermessage.Hook("angGoal", function(msgData) angGoal = msgData:ReadAngle() end )
usermessage.Hook("inThird", function(msgData) inThird = msgData:ReadBool() end )
usermessage.Hook("updateLives", function (msgData) lives = msgData:ReadLong() print("lives Update: " .. tostring(lives)) end )
usermessage.Hook("updateScore", function(msgData) score = msgData:ReadLong() end)
usermessage.Hook("shipVel", function(msgData) shipVel = msgData:ReadVector() end)
usermessage.Hook("firmCam", function(msgData) viewAng = angGoal curCamPos = camPos end)
usermessage.Hook("deathCam", function(msgData) deathCam = msgData:ReadBool() end)
usermessage.Hook("scores", 
					function(msgData) 
						local shouldClear = msgData:ReadBool()
						--print("Should Clear: " .. tostring(shouldClear))
						if shouldClear then
							HighScores = {}
						end
						local whom = msgData:ReadString()
						local points = msgData:ReadLong()
						local entry = {whom, points}
						table.insert(HighScores, entry)
					end)
usermessage.Hook("mapData", 
					function(msgData)
						local shouldClear = msgData:ReadBool()
						if shouldClear then mapData = {} end
						local dataType = msgData:ReadChar()
						local mapPos =  msgData:ReadVector()
						local dotSize = msgData:ReadChar()
						if !(dataType == nil) or
						   !(mapPos == nil) or
						   !(dotSize == nil) then
								table.insert(mapData, {dataType, mapPos, dotSize})
						end
					end)

function GenerateView(Panel)
	Panel:SetCamPos(Vector(0,0,600))
	Panel:SetLookAt(Vector(0,0,0))
end


function asInitialize()

	for i = 0, (MAX_LIVES - 1) do 
		if !(lifeShips[i + 1] == nil) then lifeShips[i + 1]:Remove() end
		lifeShips[i + 1] = vgui.Create("DModelPanel")
		lifeShips[i + 1]:SetModel("models/as_ship/as_ship.mdl")
		lifeShips[i + 1]:SetPos(10 + ( i * 40) ,55)
		lifeShips[i + 1]:SetSize(40,40)
		GenerateView(lifeShips[i + 1])
		lifeShips[i + 1]:SetVisible(false)
		lifeShips[i + 1]:SetAnimated(false)
		lifeShips[i + 1]:GetEntity():SetMaterial("models/wireframe")
		lifeShips[i + 1].LayoutEntity = function( Entity ) end
	end

	surface.CreateFont("courier", 23, 800, true, false, "HighScore", false, true, 0.75)
	surface.CreateFont("courier", 19, 500, true, false, "tabFont", false, true, 0)
	
end
hook.Add("Initialize", "as_init", asInitialize)

function string.create(chr, run)
	
	local retStr = ""
	
	for i = 1, run do
	
		retStr = retStr .. chr
	
	end
	
	return retStr
end

function showHighScores(msgData)
	
	if closingScores or openingScores then return end
	if !showScores then
		RunConsoleCommand("get_scores")
		local myScore = msgData:ReadLong()
		timer.Simple(1, function()
			HighMenu = vgui.Create("DPanel")
			
			HighMenu:SetBGColor(64,64,64,227)
			
			local vOffset = 0
			local vPad = 5
			local hPad = 50
			local hSize = 0
			local totalScores = table.Count(HighScores)
			local scoreColor = Color(255,255,255,255)
			local pointSpace = 0
			local highScore = false
			
			for k,v in pairs(HighScores) do
				local scoreEntry = vgui.Create("DLabel", HighMenu)
				scoreEntry:SetPos(hPad / 2, vOffset + vPad)
				scoreEntry:SetFont("HighScore")
				local entryStr = string.format(" #%02d. %-s", k, v[1]) --%-30s
				entryStr = entryStr .. string.create(".", 30 - string.len(entryStr))
				if k == 1 then
					pointSpace = string.len(AddComma(v[2])) + 1
					--print(pointSpace)
				end
				entryStr = entryStr .. AddComma(v[2]) .. string.create(" ", pointSpace - string.len(AddComma(v[2]))) .. "Points" -- string.format("%-7d points", v[2])
				scoreEntry:SetText(entryStr)
				local clrAdj = 255 * (k / totalScores)
				scoreColor = Color(clrAdj, 255 , 128 + (clrAdj / 2),255)
				if k == myScore then
					scoreColor = Color(255 , 255 - clrAdj, 0, 255)
					highScore = true
				end
				scoreEntry:SetTextColor(scoreColor)
				scoreEntry:SizeToContents()
				if scoreEntry:GetWide() > hSize then hSize = scoreEntry:GetWide() end
				vOffset = vOffset + scoreEntry:GetTall() + vPad
			end
			HighMenu:SetSize(hPad + hSize, 25) -- vOffset + vPad)
			HighMenu:SetPos(ScrW() / 2 - ((hSize + hPad) / 2), (ScrH() / 2) - ((vOffset + vPad) / 2))
			HighMenu:SetVisible(true)
			slidePanelOpen(HighMenu, (vOffset + vPad) / 0.15 , vOffset + vPad, RealTime())
			local displayTime = 5
			if highScore then displayTime = 7 end
			timer.Simple(displayTime, function() slidePanelClosed(HighMenu, HighMenu:GetTall() / 0.2 ,RealTime()) end)
		end)
	else
		slidePanelClosed(HighMenu, HighMenu:GetTall() / 0.15 ,RealTime())
	end	
end
usermessage.Hook("showScores", showHighScores)

function slidePanelClosed(panel, rate, lastTime)
	
	if panel == nil then return end
	
	if lastTime == nil then lastTime = RealTime() end
	
	if panel:GetTall() > 0 then
		closingScores = true
		panel:SetSize(panel:GetWide(), panel:GetTall() - ((RealTime() - lastTime) * rate))
		lastTime = RealTime()
		timer.Simple(0,slidePanelClosed, panel, rate, lastTime)
	else
		panel:SetVisible(false)
		showScores = false
		closingScores = false
	end
	
end

function slidePanelOpen(panel, rate, height, lastTime)
	
	if panel == nil then return end
	if lastTime == nil then lastTime = RealTime() end
	local panelHeight = panel:GetTall()
	
	if  panelHeight < height then
		openingScores = true
		local newHeight = panelHeight + ((RealTime() - lastTime) * rate)
		if newHeight > height then newHeight = height end
		panel:SetSize(panel:GetWide(), newHeight)
		lastTime = RealTime()
		timer.Simple(0,slidePanelOpen, panel, rate, height, lastTime)
	else
		showScores = true
		openingScores = false
	end
	
end

local function drawScore()
	
	if !cvInShip then return end
	
	surface.SetFont("HUDNumber3")
	surface.SetTextColor(255,255,255,196)
	surface.SetTextPos(10,10)
	surface.DrawText(AddComma(score))
	local cHairPos = LocalPlayer():GetPos() + (LocalPlayer():GetForward() * 1024)
	
	if !inThird then
		cam.Start3D(camPos,viewAng)
			render.SetMaterial( Material( "sprites/hud/v_crosshair2" ) )
			render.DrawQuadEasy( camPos + (viewAng:Forward() * 2000) + (shipVel * 0.1),    --position of the rect
								 viewAng:Forward() * -1,        --direction to face in
								 64, 64,              --size of the rect
								 Color( 255, 255, 255, 255 ),  --color
								 CurTime() / 360)                     --rotate 90 degrees
		cam.End3D()
	end
	
	-- Mini Map Border
	surface.SetDrawColor(128,128,128,128)
	surface.DrawOutlinedRect(mapPosX - (mapWidth / 2), 
							 mapPosY - (mapHeight / 2), mapWidth, mapHeight)
	-- Mini Map Inside
	surface.SetDrawColor(8,8,8,128)
	surface.DrawRect((mapPosX - (mapWidth / 2)) + mapBorder,
					 (mapPosY - (mapHeight / 2)) + mapBorder , mapWidth - (mapBorder * 2), mapHeight - (mapBorder * 2))
	--Mini Map Ship
	--surface.DrawCircle(mapPosX, mapPosY, dotBase + 1, Color(0,255,0,196))
		
	if table.Count(mapData) > 0 then
		for k,v in pairs(mapData) do
			local screenVec = v[2] * mapScale
			screenVec:Rotate(mapAngle)
			if v[1] == 1 then
				surface.DrawCircle(mapPosX + screenVec.x, mapPosY + screenVec.y, (dotBase + v[3]), Color(255,64,0,196))
			end
			if v[1] == 2 then
				surface.DrawCircle(mapPosX + screenVec.x, mapPosY + screenVec.y, (dotBase + v[3]), Color(0,255,255,196))
			end
			if v[1] == 3 then
				surface.DrawCircle(mapPosX + screenVec.x, mapPosY + screenVec.y, (dotBase + v[3]), Color(255,255,0,196))
			end
		end
	end
	
end
hook.Add("HUDPaint", "scorePaint", drawScore)

local nextClientThink = CurTime()
local function clientThink() 
	
	if CurTime() < nextClientThink then return end
	
	if !cvInShip then 
		for i = 1, MAX_LIVES do
			lifeShips[i]:SetVisible(false)
		end
	else
		for i = 1, MAX_LIVES do
			if i <= lives then
				lifeShips[i]:SetVisible(true)
			else
				lifeShips[i]:SetVisible(false)
			end
		end
	end
	
	nextClientThink = CurTime() + 1
	
end
hook.Add("Think", "AsteroidsClientThink", clientThink)

local function centerPanel(panel)

	panel:SetPos( (ScrW() / 2) - (panel:GetWide() / 2), (ScrH() / 2) - (panel:GetTall() / 2))
	
end

local function showJoinMsg()

		if joinMsgOpen then return end
		
		local msgBorder = 25
		joinMsg = vgui.Create("DPanel")
			
		joinMsg:SetBGColor(128,128,128,227)
		
		local joinText = vgui.Create("DLabel", joinMsg)
		joinText:SetPos(msgBorder, msgBorder)
		joinText:SetText([[Welcome To Asteroids!		
		This is a 3D remake of the classic 2D arcade game Asteroids.
		The object is to destroy as many asteroids as you can before you run out of ships.
		Whenever you shoot a large asteroid it breaks in to smallers ones.
		
		You get a new ship every ]] .. AddComma(EXTRA_LIFE_SCORE) .. [[ points.

		The largest asteroid is worth ]] .. tostring(math.Round(ASTEROID_POINTS / 5)) .. [[ points.
		The large asteroid is worth ]] .. tostring(math.Round(ASTEROID_POINTS / 4)) .. [[ points.
		The medium asteroid is worth ]] .. tostring(math.Round(ASTEROID_POINTS / 3)) .. [[ points.
		The small asteroid is worth ]] .. tostring(math.Round(ASTEROID_POINTS / 2)) .. [[ points.
		The smallest asteroid is worth ]] .. tostring(math.Round(ASTEROID_POINTS / 1)) .. [[ points.
		
		Controls:
		* You use the normal movement keys to move the ship.
		  Forward, Back, Strafe Right, Strafe Left
		* You can use the mouse to move the ship, or the Look keys.
		* You use Primary Attack to shoot.
		* You can use the Sprint key to get a speed boost, while holding it.
		* Press F1 to see this message again.
		* Press F2 to see the High Scores list.
		* You can press Use and Reload at the same time to quit the game early.
		  You will get 500 extra points for each extra ship you still have.
		]])
		joinText:SizeToContents()
		joinMsg:SetSize(joinText:GetWide() + (msgBorder * 2), joinText:GetTall() + (msgBorder * 2))
		centerPanel(joinMsg)
		joinMsg:SetVisible(true)
		joinMsgOpen = true
		timer.Simple(10, function() joinMsg:SetVisible(false) joinMsg:Remove() joinMsgOpen = false end)

end
usermessage.Hook("joinMsg", showJoinMsg)

function GM:ScoreboardShow()
	showTabMenu = true
	showCurScores()
	
	return false
end

function GM:ScoreboardHide()
	showTabMenu = false
	tabMenu:SetVisible(false)
end

function showCurScores( )
	
	local playerTable = {}
	
	local columnPad = 4
	
	local columns = { {"Rank", 8, function(val) return string.format("%02d", val) end } , 
	                  {"Player Name", 15, function(val) return val end}, 
                      {"Ping", 8, function(val) return tostring(val) end },
					  {"Current Score", 17, function(val) return AddComma(val) end },
					  {"Lives", 9, function(val) return tostring(val) end }					  }
	
	for n, ply in pairs(player.GetAll()) do
	
		print("Player : " .. ply:Nick() .. " Ping : " .. ply:Ping() .. " Score : " .. ply:Frags())
		local rankLen = string.len(columns[1][3](n))
		local nickLen = string.len(columns[2][3](ply:Nick()))
		local pingLen = string.len(columns[3][3](ply:Ping()))
		local scoreLen = string.len(columns[4][3](ply:Frags()))
		local deathLen = string.len(columns[5][3](ply:Frags()))
		
		if rankLen + columnPad >= columns[1][2] then 
			columns[1][2] = rankLen + columnPad 
		end
		if nickLen + columnPad >= columns[2][2] then 
			columns[2][2] = nickLen + columnPad 
		end
		if pingLen + columnPad >= columns[3][2] then 
			columns[3][2] = pingLen + columnPad 
		end
		if scoreLen + columnPad >= columns[4][2] then 
			columns[4][2] = scoreLen + columnPad 
		end
		if deathLen + columnPad >= columns[5][2] then 
			columns[5][2] = deathLen + columnPad 
		end

		table.insert(playerTable, { n , ply:Nick(), ply:Ping(), ply:Frags(), ply:Deaths() })
		
	end
	
	table.SortByMember(playerTable, 3)
	
	if !(tabMenu == nil) then tabMenu:Remove() end
	
	tabMenu = vgui.Create("DPanel")
			
	tabMenu:SetBGColor(0,255,0,227)
	
	local vOffset = 0
	local vPad = 5
	local hPad = 10	
	local hSize = 0
	local totalPlayers = table.Count(playerTable)
	local playerColor = Color(255,255,255,255)
	local pointSpace = 0
	local highScore = false
	
	local header = vgui.Create("DLabel", tabMenu)
	header:SetPos( hPad / 2, vOffset + vPad)
	header:SetFont("tabFont")
	local headStr = ""
	local lenAcc = 0
	for colIndex = 1, table.Count(columns) do
		headStr = headStr .. columns[colIndex][1]
		lenAcc = lenAcc + columns[colIndex][2]
		print("head lenAcc: " .. tostring(lenAcc))
		print("colIndex: " .. tostring(colIndex))
		headStr = headStr .. string.create(" ", math.abs(string.len(headStr) - lenAcc))
	end
	header:SetText(headStr)
	header:SetColor(Color(0,255,0,255))
	header:SizeToContents()
	hSize = header:GetWide()
	vOffset = vOffset + header:GetTall() + vPad
	
	for k, v in pairs(playerTable) do
		local playerEntry = vgui.Create("DLabel", tabMenu)
		playerEntry:SetPos(hPad / 2, vOffset + vPad)
		playerEntry:SetFont("tabFont")
		local entryStr = ""
		lenAcc = 0
		for colIndex = 1, table.Count(columns) do
			entryStr = entryStr .. columns[colIndex][3](v[colIndex])
			lenAcc = lenAcc + columns[colIndex][2]
			print("entry lenAcc: " .. tostring(lenAcc))
			print("colIndex: " .. tostring(colIndex))
			entryStr = entryStr .. string.create(" ", math.abs(string.len(entryStr) - lenAcc))
		end
		playerEntry:SetText(entryStr)
		local clrAdj = 255 * (k / totalPlayers)
		playerColor = Color(clrAdj, 255 , 0 , 255 - (clrAdj * 0.25))
		playerEntry:SetTextColor(playerColor)
		playerEntry:SizeToContents()
		if playerEntry:GetWide() > hSize then hSize = playerEntry:GetWide() end
		vOffset = vOffset + playerEntry:GetTall() + vPad
	end
	
	tabMenu:SetSize(hPad + hSize, vOffset + vPad)
	tabMenu:SetPos((ScrW() / 2) - ((hSize + hPad) / 2), (ScrH() / 2) - ((vOffset + vPad) / 2))
	tabMenu:SetVisible(true)
	if showTabMenu then
		timer.Simple(0.5,  showCurScores) 
	else
		tabMenu:SetVisible(false)
	end
	
end
